/**
 *    这篇规范描述了应用持续利用后台进程的方法，包括钩子，以便应用在脱机时自启动。
 *    这个系统的核心是一个事件驱动的 Web 工作线程，用于响应来自当前文档和其他源的事件。是一个用户管理
 * 安装、版本、和升级的系统。
 *    服务线程(service worker)是一个与事件驱动的后台处理相关的实体点，它可以被其他规范所扩展。
 */

/**
 * 动机：
 *    传统上 web 应用默认认为网络是可访问的。各个平台中充斥着这样的臆断。HTML 文档通过 HTTP 请求加载完毕后，
 * 再通过随后的 HTTP 请求加载所有子资源，这种方式让 web 内容与其他技术栈相比处于劣势。
 * 
 *    服务线程被设计用于处理的第一个问题就是上面的问题，通过提供一个 web 工作线程上下文，在导航访问到
 * 当前地址时在运行中可以启动这个线程。这个事件驱动的工作线程通过一个源和地址进行注册，意味着它在导航到当前
 * 地址是可以被查询。网络请求产生的事件被分发到工作线程中，响应由工作线程产生，可能会覆盖默认的网络栈行为。
 * 概念上，这将服务线程放在了网络请求和文档渲染之间，允许服务线程提供内容给文档，即使处于脱机状态
 * 
 *    熟悉之前解决离线问题的开发者认为原来的解决方案缺乏灵活性。因此，服务线程被设计为高度可编程的，提供了
 * 最大的灵活性，同时也带来了额外的复杂度。一部分复杂度来自于解决单线程模式下的服务线程响应性能问题。为了
 * 解决这个问题，API 暴露了几乎所有异步操作，这与 JavaScript 另外一些上下文很像，但强调了避免阻塞文档和
 * 资源加载的需求。
 * 
 *    服务线程设计原理的关键是错误要可恢复。很多服务线程的更新处理的细节被设计为避免这种冒险。
 * 
 *    服务线程的开始和保持激活都依靠与之关联的事件，而不是文档对象。这种设计很大程度上借鉴了开发者和第三方
 * 在使用分享线程(Shared Workers)和Chrome的后台页面时的体验。从这些系统中得到的关键教训是在执行后台处理
 * 上下文时必须要一个时间限制，既要节约资源，又要保证后台上下文丢失和重启是开发者的首要任务。服务线程通过
 * UA 创建不需要附加文档对象，也可以在任何时间点被 UA 终止。推荐开发者注意服务线程可能在一秒内被启动和
 * 杀死多次。
 * 
 *    服务线程是一个在某一源上运行的共有的、事件驱动的、时间限制的脚本上下文。这些属性使得服务线程成为
 * 一系列运行时服务的自然终止点，其上下文可能比特定文档的寿命更长。其可处理的运行时服务包括处理推送通知、
 * 异步后台数据、来自外部源的资源请求响应或者是耗时的计算任务。
 */

// 下面是一个简单示例，展示了如何启动一个服务线程
const bootstrappingServiceWorker = () => {
  /**
   * 我们发现 Chrome 中启动会抛出 An SSL certificate error occurred when fetching the script. 异常，这与 Chrome 检查本地 https 证书有关，
   * FireFox 上没有这个问题，理论上可以通过添加 Chrome 的启动参数 --unsafely-treat-insecure-origin-as-secure=允许的源地址 来添加信任
   */
  navigator.serviceWorker.register('/javascripts/webapi/serviceWorker/serviceworker.js')
    .then(registration => {
      console.log(registration);
      console.log('success~');
      if(registration.installing) {
        registration.installing.postMessage(`Howdy from your installing page.`);
      }
    }, err => {
      console.error(`Installing the worker failed!`, err);
    })
}
bootstrappingServiceWorker();

/**
 * ServiceWorker 接口
 *    [SecureContext, Exposed=(Window, Wroker)]
 *    interface ServiceWorker : EventTarget {
 *        readonly attribute USVString scriptURL;
 *        readonly attribute ServiceWorkerState state;
 *        void postMessage(any message, sequence<object> transfer)
 *        void postMessage(any message, optional PostMessageOptions options);
 *    事件：
 *        attribute EventHandler onstatechange;
 *    }
 *    enum ServiceWorkerState {
 *        'parsed', 'installing', 'installed', 'activating', 'activated', 'redundant'
 *    }
 *    一个 ServiceWorker 对象代表了一个服务线程，每个 ServiceWorker 对象都与一个服务线程关联。
 * 跨文档和工作线程实现 ServiceWorker 的多个独立对象可以同时关联到同一个服务线程中去。
 *    一个 ServiceWorker 对象有一个与服务线程的状态属性关联的 ServiceWorkerState 对象。
 * 
 * scriptURL: 返回服务线程的连续的脚本地址
 * state: 返回最近设置的 ServiceWorker 状态（值为枚举类型 ServiceWorkerState）
 */
// 下面的示例展示了如何获取服务线程的 scriptURL
var getScriptURL = () => {
  console.log(navigator.serviceWorker.controller.scriptURL);
}

/**
 *    ServiceWorkerRegistration 接口
 *    [SecureContext, Exposted=(Window, Worker)]
 *    interface ServiceWorkerRegistration : EventTarget {
 *        readonly attribute ServiceWorker? installing;
 *        readonly attribute ServiceWorker? waiting;
 *        readonly attribute ServiceWorker? active;
 *        [SameObject] readonly attrubite NavigationPreloadManager navigationPreload;
 *        readonly attribute USVString scope
 *        readonly attribute ServiceWorkerUpdateViaCache updateViaCache
 *        [NewObject] Promise<void> update()
 *        [NewObject] Promose<void> unegister()
 *        // 事件
 *        attribute EventHandler onupdatefound
 *    }
 * installing/waiting/active: 这三个属性只有最后被赋值的那个会有值，在一定范围内，一个服务线程只会有一个 ServiceWorker 对象
 * navigationPreload: 返回服务线程注册对象的 NavigationPreloadManager 对象。
 * scope: 返回注册服务线程的初始化作用域url
 * updateViaCache: 必须返回注册服务线程的通过缓存升级的模式
 * update:
 * unregister: 注销服务线程注册。
 */

/**
 * 扩展 Navigator 接口 和 WorkerNavigator 接口
 *    partial interface Navigator(WorkerNavigator) {
 *        [SecureContext, SameObject] readonly attribute ServiceWorkerContainer serviceWorker
 *    }
 * 
 * ServiceWorkerContainer 接口
 *    [SecureContext, Exposed=(Window,Worker)] {
 *        readonly attribute ServiceWorker? controller;
 *        readonly attribute Promise<ServiceWorkerRegistration> ready;
 *        [NewObject] Promise<ServiceWorkerRegistration> register(USVString scriptURL, optional RegistrationOptions options);
 *        [NewObject] Promise<FrozenArray<ServiceWorkerRegistration>> getRegistrations()
 *        void startMessage();
 *        // 事件
 *        attribute EventHandler oncontrollerchange;
 *        attribute EventHandler onmessage;
 *        attribute EventHandler onmessageerror;
 *    }
 * RegistrationOptions 字典 {
 *    USVString scope;
 *    WorkerType type = 'classic';
 *    ServiceWorkerUpdateViaCache updateViaCache = 'imports';
 * }
 * controller: 如果上下文对象（服务线程客户端）的激活态服务线程为null，则返回null。否则返回当前上下文关联的设置对线中的激活的服务线程对象
 * ready: 通过Promise抛出匹配当前创建ULR的注册对象。
 * register: 在指定作用域地址上创建或更新一个服务线程注册。如果成功，服务线程注册表会将给入的 scriptURL 绑定到作用域url中。
 * getRegistration: 获取指定的客户端URL关联的设置对象创建的服务线程注册对象。
 * getRegistrations: 获取当前作用域下左右服务线程的注册对象数组
 * startMessages(): 如果客户端消息队列不可用，则必须启用消息队列。
 */

/**
 * NavigationPreloadManager 接口
 *    [SecureContext, Exposed=(Window, Worker)]
 *    interface  NavigationPreloadManager {
 *        Promise<void> enable();
 *        Promise<void> disable();
 *        Promise<void> setHeaderValue(ByteString value)
 *        Promise<NavigationPreloadState> getState();
 *    }
 * NavigationPreloadState 字典 {
 *    boolean enabled = false;
 *    ByteString headerValue;
 * }
 * enabled(): 如果当前上下文的服务线程注册对象存在，如果这个对象已激活线程，设置这个注册对象的导航预加载允许标志，并返回undefined
 * disable(): 与上同，不过是设置为false
 * setHeaderValue(value): 设置当前上线文的服务线程注册对象的预加载头值为指定参数值
 * getState(): 如果导航预加载允许标志被设置，enabled字段为true，否则为false。headerValue值为导航预加载头值
 */